package com.bxl.netty.simulator;

import com.bxl.netty.simulator.server.MsgRead;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <>
 *
 * @author baixl
 * @date 2020/11/21
 */
public class MuilThreadNioServer {
    public static class SubProcess extends Thread {
        Selector selector = null;
        private Boolean lock = true;
        AtomicInteger curIndex = new AtomicInteger(0);
        SubProcess[] chosser;


        int index = 0;
        int length = 0;
        byte[] bytes = null;
        boolean isNew = true;

        public SubProcess(SubProcess[] chosser) {
            super();
            this.chosser = chosser;
            try {
                selector = Selector.open();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void register(SocketChannel channel, int ops) {
            try {
                channel.configureBlocking(false);
                lock = false;
                selector.wakeup();
                channel.register(selector, ops,new MsgRead());
                lock = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void randomRegister(SocketChannel channel, int ops) {
            chosser[curIndex.getAndIncrement() % chosser.length].register(channel, ops);
        }

        @Override
        public void run() {
            try {
                while (true) {
                    //selector.select() 阻塞的时候，调用注册方法也会阻塞。
                    //可以改为使用阻塞队列实现
                    if (lock ? selector.select() > 0 : selector.selectNow() > 0) {
                        Set<SelectionKey> selectionKeys = selector.selectedKeys();
                        Iterator<SelectionKey> iterator = selectionKeys.iterator();
                        while (iterator.hasNext()) {
                            SelectionKey selectionKey = iterator.next();
                            iterator.remove();
                            if (selectionKey.isWritable()) {
                                //register((SocketChannel) selectionKey.channel(), SelectionKey.OP_READ);
                            } else if (selectionKey.isReadable()) {
                                //register(read(selectionKey), SelectionKey.OP_WRITE);
                                read(selectionKey);
                            }
                        }
                    }

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public  SocketChannel read(SelectionKey key){
            Object attachment = key.attachment();
            if(attachment instanceof MsgRead ){
                MsgRead read = (MsgRead)key.attachment();
                return read.read(key);
            }
            return (SocketChannel) key.channel();
        }

        /*public  SocketChannel read(SelectionKey key) {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(64);
            int count = 0;
            ByteBuffer bigBuffer = null;
            try {
                while ((count = channel.read(buffer)) > 0) {
                    
                    buffer.flip();//发送或获取数据，设置好数据后都要调用此方法
                    int position = buffer.remaining();
                    if (buffer.remaining() < 4) {
                        //恢复，继续读取
                        buffer.position(position);
                        buffer.limit(buffer.capacity());
                        continue;
                    }
                    if (isNew) {
                        length = buffer.getInt();
                        index=0;
                        //获取报文长度
                        isNew=false;
                        bytes = new byte[length];
                    }
                    while (buffer.remaining() > 0) {
                        //可读小于报文长度，读取所有
                        if (buffer.remaining() < (length - index)) {
                            int remaining = buffer.remaining();
                            buffer.get(bytes, index, buffer.remaining());
                            index = index + remaining;
                        } else {
                            //可读大于报文长度，读取报文长度
                            buffer.get(bytes, index, length - index);
                            System.out.println(Thread.currentThread().getName() + ":" + new String(bytes, "UTF-8"));
                            write(channel, "hell world ");
                            //只要不取消，就可以一直读取（不用每次都注册）。同一个channel注册到不同的select，key是不一样的。
                            //key.cancel();
                            isNew=true;
                            if (buffer.remaining()>0 && buffer.remaining() < 4) {
                                //将剩余字符转移到最前面
                                byte[] tempByte = new byte[buffer.remaining()];
                                buffer.get(tempByte);
                                buffer.position(0);
                                buffer.limit(buffer.capacity());
                                buffer.put(tempByte);
                                continue;
                            }else if(buffer.remaining() == 0){
                                
                            }else{
                                length = buffer.getInt();
                                //获取报文长度
                                bytes = new byte[length];
                                index=4;
                                isNew=false;
                            }

                        }
                    }

                }
                key.attach(index);
                //register(channel,SelectionKey.OP_READ);
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    channel.close();
                } catch (IOException e1) {

                }
            }
            return channel;
        }*/

        /*public static SocketChannel write(SocketChannel channel, String msg) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            StringBuilder sb = new StringBuilder();
            sb.append("HTTP/1.1 200 OK\r\n")
                    .append("Content-Type: text/html;charset=utf-8\r\n")
                    .append("\r\n").append(Thread.currentThread().getName()).append("\r\n")
                    .append(msg).append("\r\n");
            try {
                buffer.put(sb.toString().getBytes());
                buffer.flip();
                channel.write(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return channel;
        }*/
    }

    public static void main(String[] args) {
        int index = 3;
        SubProcess[] chosser = new SubProcess[index];
        for (int i = 0; i < index; i++) {
            SubProcess subProcess = new SubProcess(chosser);
            subProcess.setDaemon(true);
            subProcess.start();
            chosser[i] = subProcess;
        }

        AtomicInteger curIndex = new AtomicInteger(0);

        try {

            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            Selector selector = Selector.open();
            InetSocketAddress address = new InetSocketAddress(InetAddress
                    .getLocalHost(), 8090);
            serverSocketChannel.socket().bind(address);
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("server start port = " + 8090);
            while (true) {
                if (selector.select() > 0) {
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey selectionKey = iterator.next();
                        iterator.remove();
                        if (selectionKey.isAcceptable()) {
                            SocketChannel accept = ((ServerSocketChannel) selectionKey.channel()).accept();
                            chosser[curIndex.getAndIncrement() % index].register(accept, SelectionKey.OP_READ);
                        }
                    }
                }

            }

        } catch (Exception e) {

            e.printStackTrace();
        }
    }
}
